use std::{fmt::Debug, vec};

use crate::{
    asm::{ds_type::*, settings::*},
    lexer::{OpType, Token},
    parser::{
        ParserError,
        dfa::{ASTLink, NonTerm, Symbol},
        pd::{NonTermPd, PD},
    },
};

#[macro_use]
pub mod ds_type;
pub mod settings;

/**
    imm = immediate 立即数
*/

#[derive(Debug, Clone, PartialEq)]
pub enum AsmCodeGenError {
    NotParsed,
    NoRegister,
    NoCodeProvided,
    NoPDProperty,
    InvaildParam,

    UnknownStatement,
    UnknownExpression,
    UnknownFunction,
    UnknownProgram,
    UnknownFactor,
    UnknownTerm,

    NotExpression,
    NotStatement,
    NotFunction,
    NotProgram,
    NotIdenfier,
    NotFactor,
    NotTerm,

    ExpectedProduction,
    ExpectedImmediate,
    ExpectedFunctionIdentifier,
    ExpectedUnaryOperation,
    ExpectedBinaryOperation,
    ExpectedFactorOperation, //即乘或除
    ExpectedTermOperation,   //即加或减
    ExpectedNumber,

    WrongOperandSizeSuffix,
    ASTFailure(ParserError),
}

pub fn codes_to_asmdoc(cs: &Vec<Code>) -> Result<String, AsmCodeGenError> {
    let mut s: String = String::new();

    for item in cs {
        match item {
            Code::Instr(_) | Code::Raw(_) => s += "\t",
            _ => {}
        };
        let out_str = &code_to_string(&item)?;
        s.push_str(&out_str);
        if !out_str.is_empty() {
            s += "\n";
        }
    }

    Ok(s)
}

pub fn code_to_string(c: &Code) -> Result<String, AsmCodeGenError> {
    match c {
        Code::File(file_name) => Ok(format!(".file\t\"{}\"", file_name)),
        Code::Text(txt) => Ok(format!(".text\t{}", txt)),
        Code::FnLabel(fn_name) => Ok(format!(".global\t{}\n{}:", fn_name, fn_name)),
        Code::Raw(ins) => Ok(ins.to_string()),
        Code::Label(lab) => Ok(format!("{}:", lab)),
        Code::Instr(ins) => ins.to_string(),
        Code::Pass => Ok("".to_string()),
    }
}

lazy_static! {
    static ref MAIN_HEADER: Vec<Code> = vec![
        Code::FnLabel("main".to_string()),
        Code::Raw("pushq\t%rbp\n\tmovq\t%rsp, %rbp\n\tsubq\t$32, %rsp\n\tcall\t__main".to_string()),
    ];
    static ref MAIN_TAIL: Vec<Code> = vec![Code::Raw(
        "addq\t$32, %rsp\n\tpopq\t%rbp\n\tret".to_string()
    )];
}

pub fn ast_to_codes(ast: &ASTLink, set: &AsmCodeGenSettings) -> Result<Vec<Code>, AsmCodeGenError> {
    if !ast.parser_ok {
        return Err(AsmCodeGenError::NotParsed);
    }

    let mut sub: Vec<Code> = vec![];

    for y in &ast.adj {
        sub.append(&mut ast_to_codes(&y, set)?);
    }

    match &ast.sym {
        Symbol::Term(token) => match token {
            Token::Keyword(_) => Ok(vec![]),
            _ => Ok(vec![]),
        },
        Symbol::NonTerm(non_term) => match non_term {
            NonTerm::Program => Ok(program_to_codes(ast, sub)?),
            NonTerm::Function => Ok(function_to_codes(&ast, sub)?),
            NonTerm::Statements => todo!(),
            NonTerm::Statement => Ok(statement_to_codes(&ast, set)?),
            NonTerm::Exp => Ok(vec![]), // 留给语句来处理表达式，因为表达式具有返回值
            NonTerm::Term => Ok(vec![]),
            NonTerm::Factor => Ok(vec![]),
        },
    }
}

pub fn program_to_codes(ast: &ASTLink, sub: Vec<Code>) -> Result<Vec<Code>, AsmCodeGenError> {
    // 目前看来如此，未知后续如何
    Ok(sub)
}

pub fn function_to_codes(ast: &ASTLink, mut sub: Vec<Code>) -> Result<Vec<Code>, AsmCodeGenError> {
    if !is_function(&ast) {
        return Err(AsmCodeGenError::NotFunction);
    }

    let pd = get_nt_pd(ast)?;
    let prop = &pd.prop;
    let mut vcode: Vec<Code> = vec![];

    let node_name = &ast.adj[*prop.get("name").ok_or(AsmCodeGenError::NoPDProperty)?];

    // name 项应当是一个 identify 节点
    let name = get_id_str(&node_name).ok_or(AsmCodeGenError::ExpectedFunctionIdentifier)?;
    let is_main = name == "main";
    if is_main {
        // main 函数要补充入口
        vcode.append(&mut MAIN_HEADER.clone());

        // sub 是 <statements>
        vcode.append(&mut sub);

        // 收尾
        vcode.append(&mut MAIN_TAIL.clone());
    } else {
        vcode.push(Code::FnLabel(name));

        vcode.append(&mut sub);

        vcode.push(Code::ret());
    }

    Ok(vcode)
}

pub fn statement_to_codes(
    ast: &ASTLink,
    set: &AsmCodeGenSettings,
) -> Result<Vec<Code>, AsmCodeGenError> {
    let s_type = statement_type(ast)?;
    let pd = get_nt_pd(ast)?;
    let prop = &pd.prop;
    let mut vcode: Vec<Code> = vec![];

    match s_type {
        StatementType::Return => {
            let ebody_idx = prop.get("exp_body").ok_or(AsmCodeGenError::NoPDProperty)?;
            let ebody = &ast.adj[*ebody_idx];

            // 获取 return 的表达式由什么操作数保存
            // return 的表达式返回到 %eax 中
            // 中途退出的需求统一使用跳转指令执行

            match eval_expression(&ebody, set) {
                Ok(mut rt) => {
                    vcode.append(&mut rt.codes);
                    vcode.push(Code::mov(&rt.ret_op, &reg_op!(EAX)));
                    // 跳转到 ret 位置

                    Ok(vcode)
                }
                Err(e) => Err(e),
            }
        }
        _ => Err(AsmCodeGenError::NoCodeProvided),
    }
}

// 判断一个语句是什么语句
pub fn statement_type(ast: &ASTLink) -> Result<StatementType, AsmCodeGenError> {
    if !is_statement(ast) {
        return Err(AsmCodeGenError::NotStatement);
    }

    match ast.prod_id {
        Some(prod) => match prod {
            2 => Ok(StatementType::Return),
            _ => Err(AsmCodeGenError::UnknownStatement),
        },
        _ => Err(AsmCodeGenError::ASTFailure(ParserError::ASTNotConsistent)),
    }
}

/// 给出计算表达式的代码，并说明结果的操作数是谁
/// 实际上，如果表达式是一个常数，可以直接计算出来优化
pub fn eval_expression(
    ast: &ASTLink,
    set: &AsmCodeGenSettings,
) -> Result<EvalOutput, AsmCodeGenError> {
    if !is_expression(ast) {
        return Err(AsmCodeGenError::NotExpression);
    }

    let prod_id = ast
        .prod_id
        .ok_or_else(|| AsmCodeGenError::ExpectedProduction)?;

    match prod_id {
        3 => eval_term(&ast.adj[0], &set),
        4 => {
            // 4 <exp> ::= <exp> ("+" | "-") <term>
            let mut sub_exp = eval_expression(&ast.adj[0], set)?;
            // 确保中间的符号是加或减
            let ret_op = get_operation(&ast.adj[1])
                .ok_or_else(|| AsmCodeGenError::ExpectedFactorOperation)?;
            match ret_op {
                OpType::Add | OpType::Negation => {}
                _ => return Err(AsmCodeGenError::ExpectedFactorOperation),
            }
            let mut sub_term = eval_term(&ast.adj[2], set)?;

            eval_bin_op_exp(
                &mut sub_exp,
                &mut sub_term,
                &ret_op,
                Some(&ast.adj[1].index),
                set,
            )
        }
        _ => Err(AsmCodeGenError::UnknownExpression),
    }
}

/// term 是指直接与 *,/,<factor> 结合的表达式
/// 一定有产生式
pub fn eval_term(ast: &ASTLink, set: &AsmCodeGenSettings) -> Result<EvalOutput, AsmCodeGenError> {
    if !is_term(ast) {
        return Err(AsmCodeGenError::NotTerm);
    }

    let prod_id = ast
        .prod_id
        .ok_or_else(|| AsmCodeGenError::ExpectedProduction)?;

    match prod_id {
        5 => eval_factor(&ast.adj[0], set),
        6 => {
            // 6 <term> ::= <term> ("*" | "/") <factor>
            let mut sub_term = eval_term(&ast.adj[0], set)?;
            // 确保中间的符号是乘或除
            let ret_op = get_operation(&ast.adj[1])
                .ok_or_else(|| AsmCodeGenError::ExpectedFactorOperation)?;
            match ret_op {
                OpType::Mul | OpType::Div => {}
                _ => return Err(AsmCodeGenError::ExpectedFactorOperation),
            }
            let mut sub_factor = eval_factor(&ast.adj[2], set)?;

            eval_bin_op_exp(
                &mut sub_term,
                &mut sub_factor,
                &ret_op,
                Some(&ast.adj[1].index),
                set,
            )
        }
        _ => Err(AsmCodeGenError::UnknownTerm),
    }
}

/// 二元运算的表达式计算，目前支持加减乘除，隐式转换
/// 输入子表达式输出 a,b 和二元操作符以及操作符 Token 下标，以及编译器生成选项
pub fn eval_bin_op_exp(
    a: &mut EvalOutput,
    b: &mut EvalOutput,
    ret_op: &OpType,
    op_loc: Option<&usize>,
    set: &AsmCodeGenSettings,
) -> Result<EvalOutput, AsmCodeGenError> {
    let operand: Operand;
    let mut vcode = vec![];

    
    let expected_data_type = binary_on_literal(&a.data_ty, &b.data_ty, &ret_op, op_loc,false)?;

    // 进行的是直接生成代码计算
    vcode.append(&mut gen_bin_op_codes(
        a,
        b,
        expected_data_type
            .is_signed()
            .ok_or_else(|| AsmCodeGenError::ExpectedNumber)?,
        &ret_op,
    )?);
    operand = reg_op!(EAX);

    Ok(EvalOutput {
        ret_op: operand,
        data_ty: expected_data_type,
        codes: vcode,
    })
}

/// 为给定的操作数生成一段计算加减乘除二元运算的汇编代码
/// 返回值是 eax 或 edx : eax
pub fn gen_bin_op_codes(
    a: &mut EvalOutput,
    b: &mut EvalOutput,
    out_sign: bool,
    ret_op: &OpType,
) -> Result<Vec<Code>, AsmCodeGenError> {

    // 乘除法要区别符号 imul,idiv 等
    let mut vcode: Vec<Code> = vec![];
    let eax = reg_op!(EAX);
    let ebx = reg_op!(EBX);
    let ecx = reg_op!(ECX);
    let rax = reg_op!(RAX);
    let rcx = reg_op!(RCX);

    // 计算 sub_term
    vcode.append(&mut a.codes);

    // 在 win64 中，push/pop 的操作是 %rax 级别的
    // 先把 sub_term 的返回值压栈
    vcode.push(Code::push(&rax));

    // 计算 sub_factor 返回值为 %eax
    vcode.append(&mut b.codes);

    // 把 sub_term 的返回值 pop 到 %ecx
    vcode.push(Code::pop(&rcx));

    match (&ret_op, out_sign) {
        (OpType::Negation, _) => {
            // %eax = %ecx - %eax = -(%eax - %ecx)
            vcode.push(Code::sub(&ecx, &eax));
            vcode.push(Code::neg(&eax));
        }
        (OpType::Add, _) => {
            // %eax = %ecx + %eax
            vcode.push(Code::add(&ecx, &eax));
        }
        (OpType::Mul, false) => {
            // %eax = %ecx * %eax
            vcode.push(Code::mul(&ecx));
        }
        (OpType::Mul, true) => {
            // %eax = %ecx * %eax
            vcode.push(Code::imul(&ecx));
        }
        (OpType::Div, false) => {
            // %eax = %ecx / %eax

            // ebx = eax
            vcode.push(Code::mov(&eax, &ebx));
            // eax = ecx
            vcode.push(Code::mov(&ecx, &eax));
            // 扩展 eax 到 [edx:eax] 然后
            // eax = [EDX:EAX] / ebx (商)
            // edx = [EDX:EAX] % ebx (余数)
            vcode.push(Code::expand_sign_to_dx(&eax)?);
            vcode.push(Code::div(&ecx));
        }
        (OpType::Div, true) => {
            // %eax = %ecx / %eax
            // 同上
            vcode.push(Code::mov(&eax, &ebx));
            vcode.push(Code::mov(&ecx, &eax));
            vcode.push(Code::expand_sign_to_dx(&eax)?);
            vcode.push(Code::idiv(&ebx));
        }
        _ => return Err(AsmCodeGenError::ExpectedFactorOperation),
    }

    Ok(vcode)
}

/// 对常表达式的二元运算符的编译期计算，另一方面是提供类型转换的可能途径
/// 由于需要把操作符的位置告诉 warning，方便起见，把操作符位置要传入进来
/// 一般来说，用于类型转换目的调用这个函数时，warn = false 表示不警告
pub fn binary_on_literal(
    imm1: &Literal,
    imm2: &Literal,
    bin_op: &OpType,
    op_loc: Option<&usize>,
    warn: bool,
) -> Result<Literal, AsmCodeGenError> {
    // 目前只接受数字的二元运算
    let n1 = imm1
        .to_number()
        .ok_or_else(|| AsmCodeGenError::ExpectedNumber)?;
    let n2 = imm2
        .to_number()
        .ok_or_else(|| AsmCodeGenError::ExpectedNumber)?;

    let mut div0 = false;

    let (res, of) = match bin_op {
        OpType::Negation => Ok(n1.sub(&n2)),
        OpType::Add => Ok(n1.add(&n2)),
        OpType::Mul => Ok(n1.mul(&n2)),
        OpType::Div => {
            if n2.promote() == 0 {
                div0 = true;

                // 除 0 错误，给一个 undefined 值
                Ok((Number::undefined(),false))
            } else {
                Ok(n1.div(&n2))      
            } 
        },
        _ => Err(AsmCodeGenError::ExpectedBinaryOperation),
    }?;

    let _ = op_loc.is_some_and(|x| {
        if !warn {
            return false
        }

        // 二元运算符操作符可能溢出，直接报 woverflow
        if of {
            w_operation_overflow!(res, x);
        }

        // 有时，有符号数会隐式转为无符号数计算，报 wsign_conversion
        if (res.is_signed() ^ n1.is_signed()) | (res.is_signed() ^ n2.is_signed()) {
            // 符号改变
            w_operation_sign_conv!(res, x);
        }

        if div0 {
            // 除0
            w_division_by_zero!(res,x);
        }
        true
    });

    Ok(Literal::Num(res))
}

/// factor 是指只直接与unary_op，()，和数字结合的表达式
/// 一定有产生式，非常量的返回值目前为 %eax
pub fn eval_factor(ast: &ASTLink, set: &AsmCodeGenSettings) -> Result<EvalOutput, AsmCodeGenError> {
    if !is_factor(ast) {
        return Err(AsmCodeGenError::NotFactor);
    }

    let pd = get_nt_pd(ast)?;
    let prop = &pd.prop;
    let prod_id = ast
        .prod_id
        .ok_or_else(|| AsmCodeGenError::ExpectedProduction)?;
    // <factor> ::= <uint> | <int> | <float> | <double> | <char> | <string>
    // Output : %eax，无DC下默认为int
    if prop.len() == 1 {
        // 对于 prop 中只有单个字段的 factor，就认为是一个立即数
        let sub_pd = get_pd(&ast.adj[0])?;

        return match sub_pd {
            PD::UIntL(uint_lpd) => {
                let imm = imm_num!(Number::U32(
                    implicit_conv_w!(uint_lpd.val, u32, ast.adj[0].index),
                    true
                ));

                Ok(EvalOutput {
                    ret_op: imm.clone(),
                    data_ty: literal_num!(U32(0, true)),
                    codes: vec![Code::mov(&imm, &reg_op!(EAX))],
                })
            }
            _ => Err(AsmCodeGenError::ExpectedImmediate),
        };
    }

    // 直接判 prod_id，按数组下标直接定位
    match prod_id {
        8 => {
            // 8 <factor> ::= <unary_op> <factor>
            // Output : %eax （含变量） / 立即数（常量优化）
            let unary_op = get_operation(&ast.adj[0])
                .ok_or_else(|| AsmCodeGenError::ExpectedUnaryOperation)?;

            let mut sub_exp = eval_factor(&ast.adj[1], set)?;

            eval_unary_op(&mut sub_exp, &unary_op, set)
        }

        9 => eval_expression(&ast.adj[1], &set),
        _ => Err(AsmCodeGenError::UnknownFactor),
    }
}

/// 一元运算的表达式计算
/// 输入子表达式输出 a,b 和一元操作符以及编译器生成选项
pub fn eval_unary_op(
    a: &mut EvalOutput,
    unary_op: &OpType,
    set: &AsmCodeGenSettings,
) -> Result<EvalOutput, AsmCodeGenError> {
    let mut vcode: Vec<Code> = vec![];

    let ret = if set.eval_constant_expression && a.ret_op.is_imm() {
        // 优化——编译期计算：如果子表达式是立即数，那么本表达式也是立即数
        let imm = get_imm(&a.ret_op).unwrap();
        let operand = imm!(unary_on_literal(&imm, &unary_op)?);

        Ok(operand)
    } else {
        // 直接添加计算代码
        vcode = gen_unary_op_codes(a, &unary_op)?;
        Ok(reg_op!(EAX))
    }?;

    // 实际上对于多重的-~!也能优化
    // 比如，
    //      ==> -~a = ~(~a) + 1 = a + 1 = asm {add $1,a}
    //      ==> ~-a = ~(~a + 1) = a - 1 = asm {sub $1,a}
    //      ==> ~~a = a
    //      ==> --a = a
    //      ==> !!a = asm {cmp $0,a;setne a::al}
    //      等

    Ok(EvalOutput {
        ret_op: ret,
        data_ty: unary_on_literal(&a.data_ty, &unary_op)?,
        codes: vcode,
    })
}

/// 为给定的操作数生成一段计算加减乘除二元运算的汇编代码
/// 返回值是 eax
pub fn gen_unary_op_codes(
    sub_exp: &mut EvalOutput,
    unary_op: &OpType,
) -> Result<Vec<Code>, AsmCodeGenError> {
    let eax = reg_op!(EAX);
    let mut vcode: Vec<Code> = vec![];
    vcode.append(&mut sub_exp.codes);

    match unary_op {
        OpType::Negation => {
            // neg %eax
            vcode.push(Code::neg(&eax));
        }
        OpType::BitwiseComplement => {
            // not %eax
            vcode.push(Code::not(&eax));
        }
        OpType::LogicalNegation => {
            // cmp $0,%eax      (0 - %eax == 0) ==> ZF
            // mov $0,%eax      %eax = 0
            // sete %al         if ZF then %al = 1
            let zero = &imm_num!(Number::U32(0, false));
            vcode.push(Code::cmp(zero, &eax));
            vcode.push(Code::mov(zero, &eax));
            vcode.push(Code::sete(&reg_op!(AL)));
        }
        _ => return Err(AsmCodeGenError::ExpectedUnaryOperation),
    }
    Ok(vcode)
}

/// 对常表达式的unary编译期计算，另一方面是提供类型转换的可能途径
pub fn unary_on_literal(imm: &Literal, unary_op: &OpType) -> Result<Literal, AsmCodeGenError> {
    if let Literal::Num(n) = imm {
        // 是一个数字立即数
        // 目前认为 unary 操作符不溢出
        match unary_op {
            OpType::Negation => Ok(Literal::Num(n.neg())),
            OpType::BitwiseComplement => Ok(Literal::Num(n.not())),
            OpType::LogicalNegation => Ok(Literal::Num(n.is_zero())),
            _ => Err(AsmCodeGenError::ExpectedUnaryOperation),
        }
    } else {
        // 可能有 const char* ?
        Err(AsmCodeGenError::ExpectedNumber)
    }
}

pub fn get_operation(ast: &ASTLink) -> Option<OpType> {
    match &ast.sym {
        Symbol::Term(token) => match token {
            Token::Operation(op_type) => Some(op_type.clone()),
            _ => None,
        },
        _ => None,
    }
}

pub fn is_statement(ast: &ASTLink) -> bool {
    Symbol::NonTerm(NonTerm::Statement) == ast.sym
}

pub fn is_expression(ast: &ASTLink) -> bool {
    Symbol::NonTerm(NonTerm::Exp) == ast.sym
}

pub fn is_function(ast: &ASTLink) -> bool {
    Symbol::NonTerm(NonTerm::Function) == ast.sym
}

pub fn is_term(ast: &ASTLink) -> bool {
    Symbol::NonTerm(NonTerm::Term) == ast.sym
}

pub fn is_factor(ast: &ASTLink) -> bool {
    Symbol::NonTerm(NonTerm::Factor) == ast.sym
}

pub fn get_id_str(ast: &ASTLink) -> Option<String> {
    if let Symbol::Term(Token::Identifier(s)) = &ast.sym {
        Some(s.to_string())
    } else {
        None
    }
}

pub fn get_pd(ast: &ASTLink) -> Result<&PD, AsmCodeGenError> {
    ast.pd
        .as_ref()
        .ok_or(AsmCodeGenError::ASTFailure(ParserError::ASTNoPD))
}

pub fn get_nt_pd(ast: &ASTLink) -> Result<&NonTermPd, AsmCodeGenError> {
    ast.pd
        .as_ref()
        .and_then(|pd| {
            if let PD::NonTerm(non_term_pd) = pd {
                Some(non_term_pd)
            } else {
                None
            }
        })
        .ok_or(AsmCodeGenError::ASTFailure(ParserError::ASTNoPD))
}
